﻿<?php
/* type definitions for the parser, and converter */
define("SPLIT_NUMBER", 0);
define("SPLIT_VARIABLE", 1);
define("SPLIT_ASSIGNEQUALS", 2);
define("SPLIT_PRINT", 3);
define("SPLIT_STRING", 4);
define("SPLIT_SEMICOLON", 5);
define("SPLIT_PLUS", 6);
define("SPLIT_MULTIPLY", 7);
define("SPLIT_ECHO", 8);
define("IS_OPERATOR", 0);
define("IS_OPERAND", 1);


class token {
	public $type;
	public $token;
	public $val;
	
	public function __construct($type, $token, $val) {
		$this->token = $token;
		$this->type = $type;
		$this->val = $val;
	}
}

function getval($token) {
	GLOBAL $variables;
	
	if ($token->token == SPLIT_VARIABLE) {
		return $variables[$token->val];
	} else {
		return $token->val;
	}
}

function execute(&$stack) {
	GLOBAL $variables;
	
	$operator = array_pop($stack);
	
	if($stack[count($stack) - 1]->type == IS_OPERATOR) {
		$right = execute($stack);
	} else {
		$right = array_pop($stack);
	}
	
	if (count($stack)) {
		if ($stack[count($stack) - 1]->type == IS_OPERATOR) {
			$left = execute($stack);
		} else {
			$left = array_pop($stack);
		}
	}
	
	switch($operator->token) {
		case SPLIT_ASSIGNEQUALS:
			$variables[$left->val] = getval($right);
			break;
		case SPLIT_PLUS:
			return new token(IS_OPERAND, SPLIT_NUMBER, getval($left) + getval($right));
		case SPLIT_MULTIPLY:
			return new token(IS_OPERAND, SPLIT_NUMBER, getval($left) * getval($right));
		case SPLIT_PRINT:
		case SPLIT_ECHO:
			print getval($right);
			print "\n";
	}
}

function main () {
	GLOBAL $lasttoken;
	
	while (1) {
		$stack = array();
		$operators = array();
		
		do {
			$token = gettoken();
			
			switch($token) {
				case SPLIT_NUMBER:
				case SPLIT_VARIABLE:
				case SPLIT_STRING:
					$stack[] = new token(IS_OPERAND, $token, $lasttoken);
					break;
				default:
					if ($token != SPLIT_SEMICOLON) {
						$operators[] = new token(IS_OPERATOR, $token, NULL);
					}
				}
		} while ($token != SPLIT_SEMICOLON);
			
		// move operators to stack
		while(count($operators)) array_push($stack, array_pop($operators));
		
		// execute the line!
		execute($stack);
	}
}

function gettoken() {
	GLOBAL $script, $characters, $lasttoken;
	$c = 0;
	
	// delete whitespace
	while (($c = fgetc($script)) == ' ' || $c == "\t" ||  $c == "\n" || $c == "\r");
	
	// exit if EOF is reached
	if (feof($script)) exit;
	
	// match numbers
	if (is_numeric($c)) {
		$nextchar = fgetc($script);
		
		while (is_numeric($nextchar)) {
			$c .= $nextchar;
			$nextchar = fgetc($script);
		}
	
		// the last character read was not a number, put it back
		fseek($script, -1, SEEK_CUR);
		$lasttoken = $c;
		return SPLIT_NUMBER;
	}
	
	if ($c == "=") {
		return SPLIT_ASSIGNEQUALS;
	}
	if ($c == "+") {
		return SPLIT_PLUS;
	}
	if ($c == "*") {
		return SPLIT_MULTIPLY;
	}
	if ($c == ";") {
		return SPLIT_SEMICOLON;
	}
	if ($c == "\"") {
		$nextchar = fgetc($script);
		
		while ($nextchar != "\"") {
			if ($nextchar == "\n") {
				die("Fatal error: Undetermined string\n");
			}
			$c .= $nextchar;
			$nextchar = fgetc($script);
		}
		
		// note, we don't put the last character back here as it is the closing double-quote
	    // trim off the double quote at the beginning
		$lasttoken = trim($c, "\" \t\n\r");
		return SPLIT_STRING;
	}
	if (is_string($c)) {
		$nextchar = fgetc($script);
		while($nextchar != "\n" && in_array($nextchar, $characters)) {
			$c .= $nextchar;
			$nextchar = fgetc($script);
		}

		// last character was not a letter, put it back
		fseek($script, -1, SEEK_CUR);
		$lasttoken = trim($c);

		// is this a print statement? If so, it's special
		switch($lasttoken) {
			case "print":
				return SPLIT_PRINT;
				break;
			case "echo":
				return SPLIT_ECHO;
				break;
			default:
				return SPLIT_VARIABLE;
		}
	}
}


/* the file to open to convert */
$script = fopen("script.foo", "r");

/* defines the characters allowed for variables */
$characters = array_merge(range('a', 'z'), range('A', 'Z'));
$characters[] = "_";

/* a place to store the variables */
$variables = array();

function cleanup() {
	GLOBAL $script;
	
	fclose($script);
}

	register_shutdown_function("cleanup");

	main();

?>